﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Linq;
using System.Windows.Input;
using RG.Common.Configuration;
using RG.Common.WPF.Commands;
using Microsoft.Practices.Unity;

namespace RG.Common.WPF.Filters
{
    /// <summary>
    /// Список галочек. Сделал абстрактным, потому что не умеет сериализоваться в [PropertySetting]
    /// </summary>
    [SupportDataBinding]
    public abstract class FilterOldMultiStateFrameBaseVM<TKey> : FilterBaseParamVM<List<TKey>>
    {
        protected FilterOldMultiStateFrameBaseVM(IUnityContainer cfg) : base(cfg)
        {
            Items = new BindingList<CheckVM>();
        }

        public BindingList<CheckVM> Items { get; set; }

        /// <summary>
        /// Список отмеченных итемов, перечисленных через ';'
        /// </summary>
        public string Summary { get; set; }


        public string FormattedValue
        {
            get
            {
                var check = Items.Where(x => x.IsChecked);
                if (check.Any())
                {
                    return check.Select(x => x.Caption).Aggregate((x, y) => x + "\r\n" + y);
                }
                return EmptyText;
            }
        }

        public void AddState(CheckVM check)
        {
            Items.Add(check);
            ((INotifyPropertyChanged)check).PropertyChanged += onChecked;
            RecalcSummary();
        }

        public override void ClearValue()
        {
            foreach (var vm in Items)
                vm.IsChecked = false;
        }

        public override bool ValueEmpty()
        {
            return  Items.FirstOrDefault(i => i.IsChecked) == null;
        }

        protected void onChecked(object Sender, PropertyChangedEventArgs e)
        {
            RecalcSummary();
            CheckedChanged();
            if (OnStateChanged != null)
                OnStateChanged.Execute(this);
        }

        public virtual void CheckedChanged()
        {

        }

        private void RecalcSummary()
        {
            Summary = string.Join("; ", Items.Where(i => i.IsChecked).Select(i => i.Caption).ToArray());
        }

        #region commands

        public DelegateCommand<FilterBaseVM> OnStateChanged { get; set; }

        #endregion
    }

    /// <summary>
    /// Просто галочка
    /// </summary>
    [SupportDataBinding]
    public class CheckVM : INotifyPropertyChanged, IFirePropertyChanged
    {
        public string Caption { get; set; }
        public bool IsChecked { get; set; }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string propertyName)
        {
            var e = PropertyChanged;
            if (e != null) e(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    /// <summary>
    /// Галочка с подвешенным к ней объектом
    /// </summary>
    /// <typeparam name="T">Тип объекта</typeparam>
    [SupportDataBinding]
    public class CheckVM<T> : CheckVM
    {
        public T Id { get; set; }
    }

    /// <summary>
    /// Умеет сериализовываться
    /// </summary>
    [SupportDataBinding]
    public class FilterOldMultiStateFrameVM<TKey> : FilterOldMultiStateFrameBaseVM<TKey>
    {
        public FilterOldMultiStateFrameVM(IUnityContainer cfg) : base(cfg)
        {
        }

        public string[] GetCheckedItems()
        {
            var temp = Items.Where(i => i.IsChecked).Select(i => i.Caption).ToArray();
            return temp;
        }

        public TKey[] GetCheckedObjects()
        {
            return Items.Where(i => i.IsChecked).Select(i => ((CheckVM<TKey>)i).Id).ToArray();
        }

        private List<TKey> itemsList;

        public List<TKey> ItemsList
        {
            get { return itemsList; }
            set
            {
                itemsList = value;
                foreach (var item in itemsList)
                    AddState(new CheckVM { Caption = item.ToString(), IsChecked = true });
            }
        }

        [PropertySetting(SerializeAs = SettingsSerializeAs.Xml)]
        public string[] SavedObjects
        {
            get { return GetCheckedObjects().Select(obj => obj.ToString()).ToArray(); }
            set
            {
                Dictionary<string, CheckVM> objs =
                    Items.ToDictionary(i => ((CheckVM<TKey>)i).Id.ToString(), i => i);

                ClearValue();
                foreach (var str in value)
                    if (objs.ContainsKey(str)) objs[str].IsChecked = true;
            }
        }

        public override void ClearValueQuiet()
        {
            base.ClearValue();
            Value = null;
        }

        protected override List<TKey> ReturnDefaultEmptyValue()
        {
            return null;
        }
    }



    public class FilterNewMultiStateFrameBaseVM<TKey, TValue> : FilterOldMultiStateFrameVM<TKey, TValue>
    {
        public ICommand SelectNoneCmd { get; set; }
        public ICommand SelectAllCmd { get; set; }
        public ICommand InvertCmd { get; set; }

        public FilterNewMultiStateFrameBaseVM(IUnityContainer cfg) : base(cfg)
        {
            SelectNoneCmd = new DelegateCommand(() => { foreach (var item in Items) item.IsChecked = false; }, () => Items != null && Items.Count > 0);
            SelectAllCmd = new DelegateCommand(() => { foreach (var item in Items) item.IsChecked = true; }, () => Items != null && Items.Count > 0);
            InvertCmd = new DelegateCommand(() => { foreach (var item in Items) item.IsChecked = !item.IsChecked; }, () => Items != null && Items.Count > 0);
        }

        public override void CheckedChanged()
        {
            var temp = CheckedObjects.ToList();
            Value = temp.Any() ? CheckedObjects.ToList() : null;
        }

        public override bool ValueEmpty()
        {
            return CheckedObjects == null || !CheckedObjects.Any();
        }
    }


    public class FilterMultiStateFrameVM : FilterNewMultiStateFrameBaseVM<int, string>
    {
        public FilterMultiStateFrameVM(IUnityContainer cfg) : base(cfg)
        {
        }
    }

//    public class EnumMultiStateFilterFrameVM : NewMultiStateFilterFrameBaseVM<Enum, string>
//    {
//        public EnumMultiStateFilterFrameVM(IUnityContainer cfg)
//            : base(cfg)
//        {
//        }
//    }
    
    
    public class FilterMultiStateFrameVM<TKey> : FilterOldMultiStateFrameVM<TKey>
    {
        public FilterMultiStateFrameVM(IUnityContainer cfg) : base(cfg)
        {
        }
    }

    /// <summary>
    /// Состав итемов этого state-фильтра задаётся словарём
    /// </summary>
    [SupportDataBinding]
    public class FilterOldMultiStateFrameVM<TKey, TValue> : FilterOldMultiStateFrameVM<TKey>
    {
        private readonly Dictionary<TKey, CheckVM<TKey>> itemMap = new Dictionary<TKey, CheckVM<TKey>>();
        private Dictionary<TKey, TValue> itemDictionary;

        public FilterOldMultiStateFrameVM(IUnityContainer cfg) : base(cfg)
        {
        }

        public Dictionary<TKey, TValue> ItemDictionary
        {
            get { return itemDictionary; }
            set
            {
                itemDictionary = value;
                itemMap.Clear();
                Items.Clear();
                foreach (var pair in itemDictionary)
                {
                    var vm = new CheckVM<TKey> { Caption = pair.Value.ToString(), Id = pair.Key };
                    AddState(vm);
                    itemMap[pair.Key] = vm;
                }
            }
        }


        public void SetCheckedObjects(TKey[] objs)
        {
            foreach (var vm in Items) vm.IsChecked = false;
            foreach (var key in objs) itemMap[key].IsChecked = true;
        }

        /// <summary>
        /// Доступ к одиночным чекам фильтра оператором индексирования
        /// </summary>
        public bool this[TKey key]
        {
            get { return itemMap[key].IsChecked; }
            set { itemMap[key].IsChecked = value; }
        }

        /// <summary>
        /// Доступ ко всей коллекции отмеченных чеков
        /// </summary>
        public TKey[] CheckedObjects
        {
            get
            {
                return GetCheckedObjects();
            }
            set
            {
                SetCheckedObjects(value);
            }
        }
    }

#if NEVER
    /// <summary>
    /// Обозвал FilterMultiStateFrameVM<int,string> новым типом, т.к. обеспечивает 90% случаев.
    /// Пока это не работает, потому что имя xaml-шаблона должно совпадать с именем класса.
    /// </summary>
    public class FilterMultiIntStateFrameVM : FilterMultiStateFrameVM<int,string>
    {
    }
#endif
}